package com.hhxyjsj.framework.context;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import com.hhxyjsj.framework.annotation.Autowired;
import com.hhxyjsj.framework.annotation.Component;
import com.hhxyjsj.framework.annotation.Service;
import com.hhxyjsj.framework.proxy.LogProxy;

/**
 * IOC容器管理
 * 
 * @author Puhui
 *
 */
public class FileSystemApplicationContext {
	
	/**
	 * 具体的IOC容器
	 */
	private Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
	
	/**
	 * IOC配置文件
	 */
	private String configFilePath;

	/**
	 * IOC配置的相关信息
	 */
	private Properties config = new Properties();
	
	/**
	 * 所有需要加载到IOC容器中的类信息（包名.类名）
	 */
	private List<String> clazzList = new ArrayList<String>();
	
	
	public FileSystemApplicationContext(String configFilePath){
		this.configFilePath = configFilePath;
		init();
	}
	
	/**
	 * 容器初始化
	 */
	public void init(){
		try{
			//1、定位
			location();
			
			//2、载入
			load();
			
			//3、实例化及注册
			initAndRegister();
			
			//4、依懒注入
			populate();
			
			//5、将bean通过代理类进行代理
			registerProxy();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	/**
	 * 定位IOC配置文件 
	 * 
	 * @throws IOException
	 */
	private void location() throws IOException {
		InputStream is = null;
		try{
			is = FileSystemApplicationContext.class.getClassLoader().getResourceAsStream(this.configFilePath);
			this.config.load(is);
		}
		finally{
			if(is != null){
				is.close();
			}
		}
	
	}
	
	/**
	 * 加载需要被IOC管理的类信息
	 * @throws URISyntaxException 
	 */
	private void load() throws URISyntaxException {
		String scanPackage = config.getProperty("scanPackage");	
		String rootPath = FileSystemApplicationContext.class.getResource("/").toString();
		File file = new File(new URI(rootPath + scanPackage.replace(".", "/")));
		
		int lastIndex = scanPackage.lastIndexOf(".");
		load(lastIndex > 0 ? scanPackage.substring(0, lastIndex) : "", file);
		
	}

	/**
	 * 递归加载包路径下所有的类信息
	 * 
	 * @param file
	 */
	private void load(String scanPackage, File file){
		if(file == null || !file.exists()){
			return;
		}
		
		if(file.isFile()){
			String fileName = file.getName();
			if(fileName.endsWith(".class")){
				clazzList.add(scanPackage + "." + fileName.substring(0, fileName.length() - 6));
			}
			return;
		}
		
		for(File f : file.listFiles()){
			load(scanPackage + "." + file.getName(), f);
		}
	}

	/**
	 * 实例化及注册
	 * @throws ClassNotFoundException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	private void initAndRegister() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		for(String clazzName : clazzList){
			Class<?> clazz = Class.forName(clazzName);
			
			Service service = clazz.getAnnotation(Service.class);
			Component component = clazz.getAnnotation(Component.class);

			if(service == null && component == null || clazz.isInterface()){
				continue;
			}
			
			Object obj = clazz.newInstance();
					
			String beanName = component != null ? component.value() : service != null ? service.value() : "";
			if(beanName != null && !"".equals(beanName)){
				ioc.put(beanName, obj);
			}
			
			Class<?>[] interfaces = clazz.getInterfaces();
			//判断类没有实现任务接口，则采用类名做为key(类名的第一个字母小写)
			if(interfaces == null || interfaces.length == 0){
				ioc.put(lowercaseFirstChar(clazz.getSimpleName()), obj);
				continue;
			}
			
			for(Class<?> intClazz : interfaces){
				ioc.put(intClazz.getName(), obj);
			}
		}
		
		clazzList.clear();
	}
	
	/**
	 * 依赖注入
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	private void populate() throws IllegalArgumentException, IllegalAccessException {
		for(Entry<String, Object> entry : ioc.entrySet()){
			Object obj = entry.getValue();
			
			for(Field field : obj.getClass().getDeclaredFields()){
				Autowired autowired = field.getAnnotation(Autowired.class);
				if(autowired == null){
					continue;
				}
				
				field.setAccessible(true);
				
				String beanName = autowired.value();
				if(beanName != null && !"".equals(beanName)){
					field.set(obj, ioc.get(beanName));
					continue;
				}
								
				//判断是否是接口
				if(field.getType().isInterface()){
					field.set(obj, ioc.get(field.getType().getName()));
				}else{
					field.set(obj, ioc.get(lowercaseFirstChar(field.getType().getSimpleName())));
				}
			}
			
		}
	}
	
	/**
	 * 将Ioc容器中的所有已经实例化的bean通过代理类进行代理
	 */
	private void registerProxy(){
		for(Entry<String, Object> entry : ioc.entrySet()){
			ioc.put(entry.getKey(), Proxy.newProxyInstance(this.getClass().getClassLoader(), entry.getValue().getClass().getInterfaces(), new LogProxy(entry.getValue())));
		}
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getBean(Class<T> clazz){
		if(clazz.isInterface()){
			return (T) ioc.get(clazz.getName());
		}
		return (T) ioc.get(lowercaseFirstChar(clazz.getName()));
	}
	
	private String lowercaseFirstChar(String clazzName){
		char[] str = clazzName.toCharArray();
		str[0] += 32;
		return new String(str);
	}
}
